TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
1import { withUser, parseBody, json } from "@/lib/api";2import { getProjectDetail, updateProject, deleteProject, moveConversations, listUnassignedConversations } from "@/lib/projects/service";3import { projectBodySchema, projectActionSchema } from "@/lib/projects/schemas";45export const dynamic = "force-dynamic";67type P = { id: string };89/** GET /api/projects/:id → { project, conversations, files, prompts, stats } ; `?unassigned=1` → { conversations } outside any project. */10export const GET = withUser<P>(async ({ req, user }, { id }) => {11 if (new URL(req.url).searchParams.get("unassigned") === "1") return json({ conversations: await listUnassignedConversations(user.id) });12 return json(await getProjectDetail(user.id, id));13});1415/** PATCH /api/projects/:id (any subset of the create body) → { project } */16export const PATCH = withUser<P>(async ({ req, user }, { id }) => {17 const body = await parseBody(req, projectBodySchema.partial());18 return json({ project: await updateProject(user.id, id, body) });19});2021/** POST /api/projects/:id { action: "add-conversations" | "remove-conversations", conversationIds } → { moved } */22export const POST = withUser<P>(async ({ req, user }, { id }) => {23 const body = await parseBody(req, projectActionSchema);24 const moved = await moveConversations(user.id, body.conversationIds, body.action === "add-conversations" ? id : null);25 return json({ moved });26});2728/** DELETE /api/projects/:id — conversations are kept (project_id → null); files cascade; prompts are detached. */29export const DELETE = withUser<P>(async ({ user }, { id }) => {30 await deleteProject(user.id, id);31 return json({ ok: true });32});33